#include "swoole.h"
#include "Server.h"

static pid_t swManager_spawn_worker(swFactory *factory, int worker_id);
static int swManager_loop(swFactory *factory);
static void swManager_signal_handle(int sig);

typedef struct
{
    uint8_t reloading;
    uint8_t reload_all_worker;
    uint8_t reload_task_worker;
    uint8_t read_message;
    uint8_t alarm;

} swManagerProcess;
static swManagerProcess ManagerProcess;

//create worker child proccess
int swManager_start(swFactory *factory){
	swFactoryProcess *object = factory->object;
	swServer *serv = factory->ptr;
	pid_t pid;
	int i;

	object->pipes = calloc(serv->worker_num,sizeof(swPipe));
	if(object->pipes == NULL){
		return -1;
	}

	//worker process pipes
	for(i = 0; i < serv->worker_num; i++){
		if(swPipeUnsock_create(&object->pipes[i],1,SOCK_DGRAM) < 0){
			return -1;
		}

		serv->workers[i].pipe_master = object->pipes[i].getFd(&object->pipes[i], SW_PIPE_MASTER);
		serv->workers[i].pipe_worker = object->pipes[i].getFd(&object->pipes[i], SW_PIPE_WORKER);
		serv->workers[i].pipe_object = &object->pipes[i];

		printf("serv->workers.pipe_worker = %d\n",serv->workers[i].pipe_worker);

		swServer_store_pipe_fd(serv,serv->workers[i].pipe_object);
	}

	pid = fork();
	switch(pid){
		case 0:
			//wait master process and why???
			usleep(100000);
			swListenPort *ls = serv->listen_list;
			close(ls->sock);
			printf("manager process\n");

			printf("worker num=%d\n",serv->worker_num);

			//create worker process
			for(i = 0; i < serv->worker_num; i++){
				pid = swManager_spawn_worker(factory, i);
				if(pid < 0){
					return -1;
				}else{
					serv->workers[i].pid = pid;
				}
			}

			SwooleG.pid = getpid();
			exit(swManager_loop(factory));

			break;
		case -1:
			return -1;
		default:
			serv->gs->manager_pid = pid;
			printf("master process\n");
			break;
	}

	return 0;
}

static pid_t swManager_spawn_worker(swFactory *factory, int worker_id){
	pid_t pid;
	int ret;

	pid = fork();
	if(pid < 0){
		return -1;
	}else if(pid == 0){
		ret = swWorker_loop(factory, worker_id);
		exit(ret);
	}else{
		return pid;
	}
}

static int swManager_loop(swFactory *factory){
	//serv->onManagerStart
	printf("swManager loop\n");

	int pid,i,reload_init = 0,reload_worker_i;
	pid_t new_pid;
	int status;
	int reload_worker_num;
	pid_t reload_worker_pid = 0;
	SwooleG.use_signalfd = 0;
	memset(&ManagerProcess, 0, sizeof(ManagerProcess));
	swServer *serv = factory->ptr;

	swWorker *reload_workers;
	reload_worker_num = serv->worker_num;
	reload_workers = calloc(reload_worker_num,sizeof(swWorker));

	swSignal_add(SIGTERM, swManager_signal_handle);
	swSignal_add(SIGUSR1, swManager_signal_handle);
	swSignal_add(SIGUSR2, swManager_signal_handle);

	SwooleG.main_reactor = NULL;

	while(SwooleG.running > 0){
		pid = wait(&status);
		printf("manager wait return\n");

		//interrup by signal
		if(pid < 0){
			if (ManagerProcess.reloading == 0)
            {
                error: if (errno != EINTR)
                {
                    swSysError("wait() failed.");
                }
                continue;
            }
			else if(ManagerProcess.reload_all_worker == 1){
				if(reload_init == 0){
					reload_init = 1;
					printf("Server is reloading now.\n");
					memcpy(reload_workers, serv->workers, sizeof(swWorker) * serv->worker_num);
					reload_worker_num = serv->worker_num;
					ManagerProcess.reload_all_worker = 0;
					reload_worker_i = 0;
				}
				goto kill_worker;
			}
			else{
				goto error;
			}
		}

		//worker process exit.
		if(SwooleG.running == 1){
			for(i = 0; i < serv->worker_num; i++){
				if(pid != serv->workers[i].pid){
					continue;
				}

				while(1){
					printf("manager spawn worker\n");
					new_pid = swManager_spawn_worker(factory, i);
                    if (new_pid < 0)
                    {
                        usleep(100000);
                        continue;
                    }
                    else
                    {
                        serv->workers[i].pid = new_pid;
                        break;
                    }
				}
			}

			if(pid == reload_worker_pid){
				reload_worker_i++;
			}
		}

		kill_worker:
		if(ManagerProcess.reloading == 1){
			//reload finish
            if (reload_worker_i >= reload_worker_num)
            {
                reload_worker_pid = reload_worker_i = reload_init =  ManagerProcess.reloading = 0;
                continue;
            }
            reload_worker_pid = reload_workers[reload_worker_i].pid;
            if(kill(reload_worker_pid, SIGTERM) < 0){
				if(errno == ECHILD){
					//this happen when interrupt by signal more than one time
					reload_worker_i++;
					goto kill_worker;
				}
			}
		}
	}

	//manager process is going exit.kill worker process

	free(reload_workers);
	swSignal_none();
	//kill all child process
    for (i = 0; i < serv->worker_num; i++)
    {
        kill(serv->workers[i].pid, SIGTERM);
    }

	//wait child process
    for (i = 0; i < serv->worker_num; i++)
    {
        if (swWaitpid(serv->workers[i].pid, &status, 0) < 0)
        {
            printf("waitpid(%d) failed.\n", serv->workers[i].pid);
        }
    }

	return 0;
}

static void swManager_signal_handle(int sig){
	switch(sig){
		case SIGTERM:
			SwooleG.running = 0;
			break;
		case SIGUSR1:
			if(ManagerProcess.reloading == 0){
				ManagerProcess.reloading = 1;
				ManagerProcess.reload_all_worker = 1;
			}
			break;
		case SIGUSR2://do no think of task process
			break;
		default:
			break;
	}
}
